/*            Copyright(c) 2006-2008 Dave Sexton           *
 * http://www.codeplex.com/DocProject                      *
 * http://www.codeplex.com/DocProject/Project/License.aspx *
 ***********************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using VSLangProj80;
using DaveSexton.DocProject.Engine;
using System.ComponentModel;
using System.IO;
using System.Drawing.Design;
using System.Windows.Forms.Design;
using DaveSexton.DocProject.Sandcastle.TopicManagement;
using DaveSexton.DocProject.Sandcastle.Configuration;
using DaveSexton.DocProject.Sandcastle.ContentManagement;
using DaveSexton.DocProject.Sandcastle.Resources;

namespace DaveSexton.DocProject.Sandcastle
{
	public enum MergeXmlDocumentation
	{
		Overwrite,
		KeepSource
	}

	public class SandcastleProjectOptions : DocProjectOptions
	{
		#region Public Properties
		[TypeConverter(typeof(BuildEngineProviderNameTypeConverter))]
		public override string BuildEngineProviderName
		{
			get
			{
				return base.BuildEngineProviderName;
			}
		}

		[Category("Build"), DisplayName("Version management"), SortedProperty(20),
		Editor(typeof(SandcastleProjectOptions.VersionManagementEditor), typeof(UITypeEditor)),
		TypeConverter(typeof(SandcastleProjectOptions.VersionManagementTypeConverter)),
		Description("Specify multiple versions for each project reference and external source.")]
		public SourceVersionCollection Versions
		{
			get
			{
				if (versions == null)
				{
					SandcastleSettings settings = (SandcastleSettings) BuildController.GetEngineForProject(Project).Settings;

					versions = CreateSourceVersionCollection(settings.VersionsFile, Project.Directory, out sourceVersionNames);
				}

				return versions;
			}
		}

		[Category("Build"), DisplayName("Missing dependencies"), SortedProperty(40),
		Editor(typeof(SandcastleProjectOptions.MissingDependenciesEditor), typeof(UITypeEditor)),
		TypeConverter(typeof(SandcastleProjectOptions.MissingDependenciesTypeConverter)),
		Description("Manage a list of dependencies that cannot be detected automatically by DocProject.")]
		public IList<SystemPath> MissingDependencies
		{
			get
			{
				return missingDependencies;
			}
			private set
			{
				Project.Settings.Write("Sandcastle_MissingDependencies", string.Join("|", 
					new List<SystemPath>(value).ConvertAll(p => p.CompleteRelativeTo(Project.Directory)).ToArray()));
				
				if (BufferChanges)
					missingDependenciesBuffer = value;
				else
					CommitMissingDependencies(value);
			}
		}

		[Category("Build"), DisplayName("Use friendly HTML file names"), SortedProperty(60),
		Description("Enable this setting to have Sandcastle create file names based on the name of each topic's API instead of the much shorter GUID form.")]
		public bool UseFriendlyHtmlFileNames
		{
			get
			{
				return Project.Settings.Read("Sandcastle_UseFriendlyHtmlFileNames", false);
			}
			set
			{
				Project.Settings.Write("Sandcastle_UseFriendlyHtmlFileNames", value);
			}
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "x")]
		[Category("Build"), DisplayName("Help 1.x"), SortedProperty(80), 
		Editor(typeof(SandcastleProjectOptions.ProduceHelp1xEditor), typeof(UITypeEditor)),
		TypeConverter(typeof(SandcastleProjectOptions.ProduceHelp1xTypeConverter)),
		Description("Enable Help 1.x output to have your DocProject or DocSite produce a .chm file.")]
		public Help1xProject Help1xProject
		{
			get
			{
				if (help1xProject == null)
				{
					SandcastleSettings settings = (SandcastleSettings) BuildController.GetEngineForProject(Project).Settings;

					help1xProject = new Help1xProject(Project, settings.Help1xProjectConfigFile);
				}

				return help1xProject;
			}
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "x")]
		[Category("Build"), DisplayName("Help 2.x"), SortedProperty(100), 
		Editor(typeof(SandcastleProjectOptions.ProduceHelp2xEditor), typeof(UITypeEditor)),
		TypeConverter(typeof(SandcastleProjectOptions.ProduceHelp2xTypeConverter)),
		Description("Enable Help 2.x output to have your DocProject or DocSite produce an .HxS file.")]
		public Help2xProject Help2xProject
		{
			get
			{
				if (help2xProject == null)
				{
					SandcastleSettings settings = (SandcastleSettings) BuildController.GetEngineForProject(Project).Settings;

					string mediaDirectoryRelative = SandcastleBuildEngineProvider.Presentations[PresentationName].MediaDirectory;
					string mediaDirectoryPath = Path.Combine(settings.TargetDirectory, mediaDirectoryRelative);
					string aboutImageFile = Path.Combine(mediaDirectoryPath, settings.MSHelp2AboutImageFileName);

					help2xProject = new Help2xProject(Project, mediaDirectoryPath, mediaDirectoryRelative, 
						settings.GlobalMetadataFile, settings.Help2xNamedUrlIndexFile, settings.SpecialHelp2xHtmlTopicsDirectory, aboutImageFile);
				}

				return help2xProject;
			}
		}

		[Category("Build"), DisplayName("Build assembler options"), SortedProperty(120),
		Description("Various options that affect the behavior and performance of Build Assembler and whether diagnostic information is captured.")]
		public BuildAssemblerOptions BuildAssemblerOptions
		{
			get
			{
				return Project.Settings.Read("Sandcastle_BuildAssemblerOptions", BuildAssemblerOptions.TraceErrors | BuildAssemblerOptions.Cancelable);
			}
			set
			{
				Project.Settings.Write("Sandcastle_BuildAssemblerOptions", value);
			}
		}

		/// <summary>
		/// Gets or sets the name of the configuration file to be used by the <strong>MRefBuilder</strong> program.
		/// </summary>
		[Category("Configuration"), DisplayName("Documentation set name"), SortedProperty(20),
		Description("Specifies the base file name for build output and also the project name as it should appear in help topics.")]
		public string DocumentationSetName
		{
			get
			{
				string value;
				return ((value = Project.Settings.Read("Sandcastle_DocumentationSetName", string.Empty).Trim()).Length == 0)
					? Project.Name
					: value;
			}
			set
			{
				if (!string.Equals(DocumentationSetName, value, StringComparison.Ordinal))
				{
					Project.Settings.Write("Sandcastle_DocumentationSetName", value);

					NotifyFullBuildRequired();
				}
			}
		}

		/// <summary>
		/// Gets or sets the name of the configuration file to be used by the <strong>MRefBuilder</strong> program.
		/// </summary>
		[Category("Configuration"), DisplayName("MRefBuilder configuration file name"), SortedProperty(40),
		Description("Name of the configuration file to be used by the MRefBuilder program.")]
		public string MRefBuilderConfigurationFileName
		{
			get
			{
				return Project.Settings.Read("Sandcastle_MRefBuilderConfigurationFileName", Path.GetFileName(SandcastleBuildEngineProvider.Paths.MRefBuilderConfig));
			}
			set
			{
				if (value == null)
					throw new ArgumentNullException("value");

				Project.Settings.Write("Sandcastle_MRefBuilderConfigurationFileName", value);
			}
		}

		/// <summary>
		/// Gets or sets the name of the configuration file to be used by the <b>BuildAssembler</b> when building help 1.x.
		/// </summary>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "x")]
		[Category("Configuration"), DisplayName("Help 1.x reference configuration file name"), SortedProperty(60),
		Description("Name of the configuration file to be used by Build Assembler when building Help 1.x reference documentation.")]
		public string Help1xConfigFileName
		{
			get
			{
				return Project.Settings.Read("Sandcastle_Help1xConfigFileName", SandcastleBuildEngineProvider.ProjectPaths.Help1xConfigFileName);
			}
			set
			{
				if (value == null)
					throw new ArgumentNullException("value");

				Project.Settings.Write("Sandcastle_Help1xConfigFileName", value);
			}
		}

		/// <summary>
		/// Gets or sets the name of the configuration file to be used by the <b>BuildAssembler</b> when building help 2.x.
		/// </summary>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "x")]
		[Category("Configuration"), DisplayName("Help 2.x reference configuration file name"), SortedProperty(80),
		Description("Name of the configuration file to be used by Build Assembler when building Help 2.x reference documentation.")]
		public string Help2xConfigFileName
		{
			get
			{
				return Project.Settings.Read("Sandcastle_Help2xConfigFileName", SandcastleBuildEngineProvider.ProjectPaths.Help2xConfigFileName);
			}
			set
			{
				if (value == null)
					throw new ArgumentNullException("value");

				Project.Settings.Write("Sandcastle_Help2xConfigFileName", value);
			}
		}

		/// <summary>
		/// Gets or sets the name of the configuration file to be used by the <b>BuildAssembler</b> when building conceptual topics for help 1.x.
		/// </summary>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "x")]
		[Category("Configuration"), DisplayName("Help 1.x conceptual configuration file name"), SortedProperty(100),
		Description("Name of the configuration file to be used by Build Assembler when building Help 1.x conceptual documentation.")]
		public string Help1xConceptualConfigFileName
		{
			get
			{
				return Project.Settings.Read("Sandcastle_Help1xConceptualConfigFileName", SandcastleBuildEngineProvider.ProjectPaths.Help1xConceptualConfigFileName);
			}
			set
			{
				if (value == null)
					throw new ArgumentNullException("value");

				Project.Settings.Write("Sandcastle_Help1xConceptualConfigFileName", value);
			}
		}

		/// <summary>
		/// Gets or sets the name of the configuration file to be used by the <b>BuildAssembler</b> when building conceptual topics for help 2.x.
		/// </summary>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "x")]
		[Category("Configuration"), DisplayName("Help 2.x conceptual configuration file name"), SortedProperty(120),
		Description("Name of the configuration file to be used by Build Assembler when building Help 2.x conceptual documentation.")]
		public string Help2xConceptualConfigFileName
		{
			get
			{
				return Project.Settings.Read("Sandcastle_Help2xConceptualConfigFileName", SandcastleBuildEngineProvider.ProjectPaths.Help2xConceptualConfigFileName);
			}
			set
			{
				if (value == null)
					throw new ArgumentNullException("value");

				Project.Settings.Write("Sandcastle_Help2xConceptualConfigFileName", value);
			}
		}

		[Category("Configuration"), DisplayName("Show entire build component stack"), SortedProperty(140), RefreshProperties(RefreshProperties.All),
		Description("Indicates whether the Build Component Stack options will display all build components or just those that have an associated editor.")]
		public bool ShowEntireBuildComponentStack
		{
			get
			{
				return Project.Settings.Read("Sandcastle_ShowEntireBuildComponentStack", false);
			}
			set
			{
				if (value != ShowEntireBuildComponentStack)
				{
					NotifyChanging("ShowEntireBuildComponentStack");

					try
					{
						Project.Settings.Write("Sandcastle_ShowEntireBuildComponentStack", value);

						if (help1xReferenceComponentsManager != null)
							NotifyChanged("Help1xBuildComponents", help1xReferenceComponentsManager, help1xReferenceComponentsManager);

						if (help2xReferenceComponentsManager != null)
							NotifyChanged("Help2xBuildComponents", help2xReferenceComponentsManager, help2xReferenceComponentsManager);

						if (help1xConceptualComponentsManager != null)
							NotifyChanged("Help1xConceptualBuildComponents", help1xConceptualComponentsManager, help1xConceptualComponentsManager);

						if (help2xConceptualComponentsManager != null)
							NotifyChanged("Help2xConceptualBuildComponents", help2xConceptualComponentsManager, help2xConceptualComponentsManager);
					}
					finally
					{
						NotifyChanged("ShowEntireBuildComponentStack", !value, value);
					}
				}
			}
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "x")]
		[Category("Configuration"), DisplayName("Help 1.x reference build component stack"), SortedProperty(160), RefreshProperties(RefreshProperties.All)]
		[Description("Sandcastle build components in the Help 1.x reference configuration file.")]
		public BuildComponents.SandcastleBuildComponentManager Help1xBuildComponents
		{
			get
			{
				if (help1xReferenceComponentsManager == null)
				{
					string config = Path.Combine(Path.Combine(Path.Combine(
						Project.Directory,
						SandcastleBuildEngineProvider.ProjectPaths.RelativeHelpRoot),
						SandcastleBuildEngineProvider.ProjectPaths.Configuration),
						Help1xConfigFileName);

					help1xReferenceComponentsManager = new BuildComponents.SandcastleBuildComponentManager("Help1xBuildComponents", this, config, HelpTypes.Help1xReference);
				}

				return help1xReferenceComponentsManager;
			}
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "x")]
		[Category("Configuration"), DisplayName("Help 2.x reference build component stack"), SortedProperty(180), RefreshProperties(RefreshProperties.All)]
		[Description("Sandcastle build components in the Help 2.x reference configuration file.")]
		public BuildComponents.SandcastleBuildComponentManager Help2xBuildComponents
		{
			get
			{
				if (help2xReferenceComponentsManager == null)
				{
					string config = Path.Combine(Path.Combine(Path.Combine(
						Project.Directory,
						SandcastleBuildEngineProvider.ProjectPaths.RelativeHelpRoot),
						SandcastleBuildEngineProvider.ProjectPaths.Configuration),
						Help2xConfigFileName);

					help2xReferenceComponentsManager = new BuildComponents.SandcastleBuildComponentManager("Help2xBuildComponents", this, config, HelpTypes.Help2xReference);
				}

				return help2xReferenceComponentsManager;
			}
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "x")]
		[Category("Configuration"), DisplayName("Help 1.x conceptual build component stack"), SortedProperty(200), RefreshProperties(RefreshProperties.All)]
		[Description("Sandcastle build components in the Help 1.x conceptual configuration file.")]
		public BuildComponents.SandcastleBuildComponentManager Help1xConceptualBuildComponents
		{
			get
			{
				if (help1xConceptualComponentsManager == null)
				{
					string config = Path.Combine(Path.Combine(Path.Combine(
						Project.Directory,
						SandcastleBuildEngineProvider.ProjectPaths.RelativeHelpRoot),
						SandcastleBuildEngineProvider.ProjectPaths.Configuration),
						Help1xConceptualConfigFileName);

					help1xConceptualComponentsManager = new BuildComponents.SandcastleBuildComponentManager("Help1xConceptualBuildComponents", this, config, HelpTypes.Help1xConceptual);
				}

				return help1xConceptualComponentsManager;
			}
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "x")]
		[Category("Configuration"), DisplayName("Help 2.x conceptual build component stack"), SortedProperty(220), RefreshProperties(RefreshProperties.All)]
		[Description("Sandcastle build components in the Help 2.x conceptual configuration file.")]
		public BuildComponents.SandcastleBuildComponentManager Help2xConceptualBuildComponents
		{
			get
			{
				if (help2xConceptualComponentsManager == null)
				{
					string config = Path.Combine(Path.Combine(Path.Combine(
						Project.Directory,
						SandcastleBuildEngineProvider.ProjectPaths.RelativeHelpRoot),
						SandcastleBuildEngineProvider.ProjectPaths.Configuration),
						Help2xConceptualConfigFileName);

					help2xConceptualComponentsManager = new BuildComponents.SandcastleBuildComponentManager("Help2xConceptualBuildComponents", this, config, HelpTypes.Help2xConceptual);
				}

				return help2xConceptualComponentsManager;
			}
		}

		[Category("Content"), DisplayName("Generate root API topic"), SortedProperty(20), 
		Description("Specifies whether a root topic is generated for reference documentation.  The root topic contains all of the namespace topics in the TOC.")]
		public bool GenerateRootApiTopic
		{
			get
			{
				return Project.Settings.Read("Sandcastle_GenerateRootApiTopic", true);
			}
			set
			{
				Project.Settings.Write("Sandcastle_GenerateRootApiTopic", value);
			}
		}

		[Category("Content"), DisplayName("Merge XML documentation"), SortedProperty(40), DefaultValue(MergeXmlDocumentation.Overwrite),
		Description("Indicates how external XML documentation generated by the Topic Editor will be merged with XML documentation comments from source code." +
		 "  Documentation is automatically merged per section; e.g., summary, remarks, example, etc.")]
		public MergeXmlDocumentation MergeXmlDocumentation
		{
			get
			{
				return Project.Settings.Read("Sandcastle_MergeXmlDocumentation", MergeXmlDocumentation.Overwrite);
			}
			set
			{
				Project.Settings.Write("Sandcastle_MergeXmlDocumentation", value);
			}
		}

		[Category("Content"), DisplayName("Topic Management"), SortedProperty(60),
		Editor(typeof(SandcastleProjectOptions.TopicManagementEditor), typeof(UITypeEditor)),
		Description("Provides tools for managing the table of contents (TOC), filtering, conceptual documentation, and reference XML documentation for the project, namespaces and API elements.")]
		public TopicManager TopicManagement
		{
			get
			{
				if (topicManagement == null)
				{
					topicManagement = new TopicManager(this);
					topicManagement.AsyncError += topicManagement_AsyncError;
				}

				return topicManagement;
			}
		}

		[Category("Content"), DisplayName("Topic Designer"), SortedProperty(80),
		Editor(typeof(SandcastleProjectOptions.TopicDesignerEditor), typeof(UITypeEditor)),
		Description("Design shared content such as the header and footer of all topics.")]
		public XmlContentManager TopicDesigner
		{
			get
			{
				if (topicDesigner == null)
				{
					topicDesigner = new XmlContentManager(Project,
						SandcastleBuildEngineProvider.Presentations[PresentationName], PresentationPathAbsolute, false);
				}

				return topicDesigner;
			}
		}

		[Category("Presentation"), DisplayName("Presentation Style"), SortedProperty(20), RefreshProperties(RefreshProperties.All),
		Description("The Sandcastle presentation style that was chosen when the DocProject or DocSite was created.")]
		public string PresentationName
		{
			get
			{
				return GetPresentationName(Project);
			}
		}

		[Category("Presentation"), DisplayName("Presentation Path"), SortedProperty(40),
		Description("Path to the presentation directory for the project.  The value can either be relative to the project's target directory, relative to the Sandcastle installation's " +
		"Presentation directory, or an entirely different absolute path.")]
		public string PresentationPath
		{
			get
			{
				string path = Project.Settings.Read<string>("Sandcastle_PresentationPath", null);

				bool success;
				return (path == null) ? DefaultDocProjectPresentationPath : EnsurePresentationPath(CleanPresentationPath(path), false, out success);
			}
			set
			{
				string path = value;
				bool success = EnsureAbsolutePresentationPath(ref path);

				Project.Settings.Write("Sandcastle_PresentationPath", path);

				if (!success)
					// Exception is thrown after the appropriate value is written above because this class is used in a PropertyGrid control, 
					// which will show a message box to the user when an exception is thrown and we don't want the user's value being replaced.
					// In this respect, the exception is more like a warning.
					throw new ArgumentException(string.Format(System.Globalization.CultureInfo.CurrentCulture,
						Errors.SandcastlePresentationPathNotFound, value), "value");

				NotifyFullBuildRequired();
			}
		}

		[Browsable(false)]
		public IDictionary<string, string> SourceVersionNames
		{
			get
			{
				return sourceVersionNames;
			}
		}

		[Browsable(false)]
		public string PresentationPathAbsolute
		{
			get
			{
				string path = Project.Settings.Read<string>("Sandcastle_PresentationPath", null);

				EnsureAbsolutePresentationPath(ref path);

				return path;
			}
		}
		#endregion

		#region Private / Protected
		private string DefaultDocProjectPresentationPath
		{
			get
			{
				if (defaultDocProjectPresentationPath == null)
				{
					bool success;
					defaultDocProjectPresentationPath = EnsurePresentationPath(SandcastleBuildEngineProvider.ProjectPaths.Presentation, false, out success);
				}

				return defaultDocProjectPresentationPath;
			}
		}

		/// <summary>
		/// Dictionary of version names for source assemblies.  The keys are generated by the <see cref="SourceVersion.GenerateKey"/> 
		/// method for each <see cref="ReferenceInfo"/> path.
		/// </summary>
		private IDictionary<string, string> sourceVersionNames, sourceVersionNamesBuffer;
		private SourceVersionCollection versions;
		private IEnumerable<SourceVersion> versionsBuffer;
		private bool versionsChanged;
		private Help1xProject help1xProject, help1xProjectBuffer;
		private Help2xProject help2xProject, help2xProjectBuffer;
		private string defaultDocProjectPresentationPath;
		private IEnumerable<SystemPath> missingDependenciesBuffer;
		private readonly List<SystemPath> missingDependencies = new List<SystemPath>();
		private TopicManager topicManagement;
		private XmlContentManager topicDesigner;
		private BuildComponents.SandcastleBuildComponentManager help1xReferenceComponentsManager, help2xReferenceComponentsManager, help1xConceptualComponentsManager, help2xConceptualComponentsManager;
		#endregion

		#region Constructors
		/// <summary>
		/// Constructs a new instance of the <see cref="SandcastleProjectOptions" /> class.
		/// </summary>
		public SandcastleProjectOptions(IDocProject project)
			: base(project)
		{
			// NOTE: Do not reference the BuildSettings for this project here or there may be an infinitely recursive loop
			// since DocProject will ask the provider to construct the BuildSettings and the BuildSettings uses the 
			// options.  If the first options are being contstructed then DocProject will ask the provider to create
			// new options again since the first constructor hasn't completed yet.  After the constructor has completed
			// the options are cached, but it will never make it that far.  Same for the settings.

			string dependencies = project.Settings.Read("Sandcastle_MissingDependencies");

			if (dependencies != null)
				foreach (string dependency in dependencies.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries))
					missingDependencies.Add(new SystemPath(Project.Directory, dependency, true));
		}
		#endregion

		#region Methods
		public static new void ImportOptions(IDocProject fromProject, IDocProject toProject, bool importExternalSources)
		{
			if (fromProject == null)
				throw new ArgumentNullException("fromProject");

			if (toProject == null)
				throw new ArgumentNullException("toProject");

			if (!(fromProject.Provider is SandcastleBuildEngineProvider))
				throw new ArgumentException(Resources.Errors.InvalidBuildEngineTypeSandcastle, "fromProject");

			if (!(toProject.Provider is SandcastleBuildEngineProvider))
				throw new ArgumentException(Resources.Errors.InvalidBuildEngineTypeSandcastle, "toProject");

			DocProjectOptions.ImportOptions(fromProject, toProject, importExternalSources);

			ImportSetting<MergeXmlDocumentation>("Sandcastle_MergeXmlDocumentation", fromProject, toProject);
			ImportSetting<bool>("Sandcastle_GenerateRootApiTopic", fromProject, toProject);
			ImportSetting<bool>("Sandcastle_ShowEntireBuildComponentStack", fromProject, toProject);

			ImportSetting("Sandcastle_Help2xConceptualConfigFileName", fromProject, toProject);
			ImportSetting("Sandcastle_Help1xConceptualConfigFileName", fromProject, toProject);
			ImportSetting("Sandcastle_Help2xConfigFileName", fromProject, toProject);
			ImportSetting("Sandcastle_Help1xConfigFileName", fromProject, toProject);
			ImportSetting("Sandcastle_MRefBuilderConfigurationFileName", fromProject, toProject);

			ImportSetting("Sandcastle_DocumentationSetName", fromProject, toProject);
			ImportSetting<BuildAssemblerOptions>("Sandcastle_BuildAssemblerOptions", fromProject, toProject);
			ImportSetting<bool>("Sandcastle_UseFriendlyHtmlFileNames", fromProject, toProject);
			ImportSetting("Sandcastle_MissingDependencies", fromProject, toProject);
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")]
		public static string GetPresentationName(IDocProject project)
		{
			return project.Settings.GetCommittedValue("Sandcastle_PresentationName");
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")]
		public static string GetSandcastleVersion(IDocProject project)
		{
			try
			{
				string value = project.Settings.GetCommittedValue("Sandcastle_Version");

				if (!string.IsNullOrEmpty(value))
					return new Version(value).ToString();
			}
			catch (ArgumentException) { }
			catch (OverflowException) { }
			catch (FormatException) { }

			return null;
		}

		internal static void SetSandcastleVersion(IDocProject project, Version version)
		{
			project.Settings.SetCommittedValue("Sandcastle_Version", version.ToString());
		}

		internal static void SetPresentation(IDocProject project, Presentation presentation)
		{
			project.Settings.SetCommittedValue("Sandcastle_PresentationName", presentation.Name);
		}

		private static string CleanPresentationPath(string path)
		{
			if (!Path.IsPathRooted(path))
				path = PathFormat.RemoveLeadingPathSeparator(path);

			if (string.IsNullOrEmpty(path))
				path = SandcastleBuildEngineProvider.ProjectPaths.Presentation;

			return path;
		}

		[System.Diagnostics.DebuggerHidden]
		private static SourceVersionCollection CreateSourceVersionCollection(string versionsFile, string verionFilesRelativeToPath, out IDictionary<string, string> sourceVersionNames)
		{
			try
			{
				return new SourceVersionCollection(SourceVersion.LoadVersions(versionsFile, verionFilesRelativeToPath, out sourceVersionNames));
			}
			catch (DirectoryNotFoundException) { }
			catch (FileNotFoundException) { }
			catch (IOException ex)
			{
				Log.Exception(ex, Resources.Text.CannotLoadVersionsFile, versionsFile);

				DocProjectEnvironment.ShowErrorDialog(Resources.Text.CannotLoadVersionsFile, versionsFile);
			}

			sourceVersionNames = new Dictionary<string, string>(0);
			return new SourceVersionCollection();
		}

		public void CancelAsynchronousBuilds()
		{
			if (topicManagement != null)
				topicManagement.CancelReferenceTopicsBuild(true);
		}

		public override void Commit()
		{
			if (versionsBuffer != null)
			{
				CommitVersions(versionsBuffer, sourceVersionNamesBuffer);
				versionsBuffer = null;
				sourceVersionNamesBuffer = null;
			}

			if (missingDependenciesBuffer != null)
			{
				CommitMissingDependencies(missingDependenciesBuffer);
				missingDependenciesBuffer = null;
			}

			if (help1xProjectBuffer != null)
			{
				CommitHelp1xProject(help1xProjectBuffer);
				help1xProjectBuffer = null;
			}

			if (help2xProjectBuffer != null)
			{
				CommitHelp2xProject(help2xProjectBuffer);
				help2xProjectBuffer = null;
			}

			// NOTE: No action is required to commit changes to properties that use editor dialogs since they are committed when OK is clicked on the dialog (they're not yet saved to disc though).
			// And if Cancel is clicked on the Tools Options dialog, then the backing store is nulled without being saved to disc.  The backing stores are then reloaded the next time they're required.

			base.Commit();
		}

		private void CommitVersions(IEnumerable<SourceVersion> commitVersions, IDictionary<string, string> commitSourceVersionNames)
		{
			versions.Clear();
			versions.AddRange(commitVersions);

			sourceVersionNames = commitSourceVersionNames;

			versionsChanged = true;
		}

		private void CommitMissingDependencies(IEnumerable<SystemPath> value)
		{
			missingDependencies.Clear();
			missingDependencies.AddRange(value);
		}

		private void CommitHelp1xProject(Help1xProject project)
		{
			help1xProject = project;
		}

		private void CommitHelp2xProject(Help2xProject project)
		{
			help2xProject = project;
		}

		public override void Cancel()
		{
			// NOTE: Do not set variables such as versionsChanged to false here since it 
			// represents changes that have already been committed, and they shouldn't be 
			// subsequently canceled.  The only way to set versionsChanged to false is to 
			// call the Save method (or just close the application without saving).

			versionsBuffer = null;
			sourceVersionNamesBuffer = null;
			missingDependenciesBuffer = null;
			help1xProjectBuffer = null;
			help2xProjectBuffer = null;

			topicDesigner = null;
			topicManagement = null;

			help1xReferenceComponentsManager = null;
			help2xReferenceComponentsManager = null;
			help1xConceptualComponentsManager = null;
			help2xConceptualComponentsManager = null;

			base.Cancel();
		}

		public override void Save()
		{
			if (versionsChanged)
			{
				SandcastleSettings settings = (SandcastleSettings) BuildController.GetEngineForProject(Project).Settings;

				string file = settings.VersionsFile;
				IProjectItem item = Project.GetItem(file);

				item.Checkout();

				SourceVersion.SaveVersions(file, Project.Directory, Versions, sourceVersionNames);

				versionsChanged = false;

				item.Show();
			}

			if (help1xProject != null)
				help1xProject.Save();

			if (help2xProject != null)
				help2xProject.Save();

			if (topicManagement != null)
				topicManagement.SaveChanges();

			if (topicDesigner != null)
				topicDesigner.SaveChanges();

			if (help1xReferenceComponentsManager != null)
				help1xReferenceComponentsManager.SaveChanges();

			if (help2xReferenceComponentsManager != null)
				help2xReferenceComponentsManager.SaveChanges();

			if (help1xConceptualComponentsManager != null)
				help1xConceptualComponentsManager.SaveChanges();

			if (help2xConceptualComponentsManager != null)
				help2xConceptualComponentsManager.SaveChanges();

			base.Save();
		}

		private bool EnsureAbsolutePresentationPath(ref string path)
		{
			bool success;
			path = EnsurePresentationPath(CleanPresentationPath(path), true, out success);

			return success;
		}

		private string EnsurePresentationPath(string path, bool makeAbsoluteIfValid, out bool success)
		{
			success = true;

			path = Environment.ExpandEnvironmentVariables(path);

			if (Path.IsPathRooted(path))
			{
				if (Directory.Exists(path))
					return path;
				else
				{
					success = false;
					return SandcastleBuildEngineProvider.DefaultPresentationFullPath;
				}
			}

			string physicalPath = Path.Combine(Project.Directory, Path.Combine(SandcastleBuildEngineProvider.ProjectPaths.RelativeHelpRoot, path));

			if (Directory.Exists(physicalPath))
			{
				if (makeAbsoluteIfValid)
					path = physicalPath;
			}
			else if (Directory.Exists(physicalPath = Path.Combine(SandcastleBuildEngineProvider.Paths.Presentation, path)))
				path = physicalPath;
			else
			{
				success = false;
				path = SandcastleBuildEngineProvider.DefaultPresentationFullPath;
			}

			return path;
		}

		// the following methods are for property grid reflection

		private bool ShouldSerializePresentationPath()
		{
			return !PresentationPath.Equals(DefaultDocProjectPresentationPath, StringComparison.CurrentCultureIgnoreCase);
		}

		private void ResetPresentationPath()
		{
			PresentationPath = DefaultDocProjectPresentationPath;
		}

		private bool ShouldSerializePresentationName()
		{
			return false;
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId = "System.Boolean.TryParse(System.String,System.Boolean@)")]
		private bool ShouldSerializeUseFriendlyHtmlFileNames()
		{
			bool value;
			bool.TryParse(Project.Settings.GetCommittedValue("Sandcastle_UseFriendlyHtmlFileNames"), out value);

			return UseFriendlyHtmlFileNames != value;
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId = "System.Boolean.TryParse(System.String,System.Boolean@)")]
		private void ResetUseFriendlyHtmlFileNames()
		{
			bool value;
			bool.TryParse(Project.Settings.GetCommittedValue("Sandcastle_UseFriendlyHtmlFileNames"), out value);

			UseFriendlyHtmlFileNames = value;
		}

		private bool ShouldSerializeBuildAssemblerOptions()
		{
			string options = Project.Settings.GetCommittedValue("Sandcastle_BuildAssemblerOptions");

			if (string.IsNullOrEmpty(options))
				return Project.Settings.ContainsKey("Sandcastle_BuildAssemblerOptions");
			else
				return BuildAssemblerOptions != (BuildAssemblerOptions) Enum.Parse(typeof(BuildAssemblerOptions), options);
		}

		private void ResetBuildAssemblerOptions()
		{
			string options = Project.Settings.GetCommittedValue("Sandcastle_BuildAssemblerOptions");

			if (!string.IsNullOrEmpty(options))
				BuildAssemblerOptions = (BuildAssemblerOptions) Enum.Parse(typeof(BuildAssemblerOptions), options);
			else
				Project.Settings.Remove("Sandcastle_BuildAssemblerOptions");
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId = "System.Boolean.TryParse(System.String,System.Boolean@)")]
		private bool ShouldSerializeGenerateRootApiTopic()
		{
			bool value;
			bool.TryParse(Project.Settings.GetCommittedValue("Sandcastle_GenerateRootApiTopic"), out value);

			return GenerateRootApiTopic != value;
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId = "System.Boolean.TryParse(System.String,System.Boolean@)")]
		private void ResetGenerateRootApiTopic()
		{
			bool value;
			bool.TryParse(Project.Settings.GetCommittedValue("Sandcastle_GenerateRootApiTopic"), out value);

			GenerateRootApiTopic = value;
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId = "System.Boolean.TryParse(System.String,System.Boolean@)")]
		private bool ShouldSerializeShowEntireBuildComponentStack()
		{
			bool value;
			bool.TryParse(Project.Settings.GetCommittedValue("Sandcastle_ShowEntireBuildComponentStack"), out value);

			return ShowEntireBuildComponentStack != value;
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId = "System.Boolean.TryParse(System.String,System.Boolean@)")]
		private void ResetShowEntireBuildComponentStack()
		{
			bool value;
			bool.TryParse(Project.Settings.GetCommittedValue("Sandcastle_ShowEntireBuildComponentStack"), out value);

			ShowEntireBuildComponentStack = value;
		}

		private bool ShouldSerializeDocumentationSetName()
		{
			return Project.Settings.Read("Sandcastle_DocumentationSetName", string.Empty).Trim().Length > 0;
		}

		private void ResetDocumentationSetName()
		{
			Project.Settings.Write("Sandcastle_DocumentationSetName", null);
		}

		private bool ShouldSerializeMRefBuilderConfigurationFileName()
		{
			return !MRefBuilderConfigurationFileName.Equals(Path.GetFileName(SandcastleBuildEngineProvider.Paths.MRefBuilderConfig), StringComparison.OrdinalIgnoreCase);
		}

		private void ResetMRefBuilderConfigurationFileName()
		{
			MRefBuilderConfigurationFileName = Path.GetFileName(SandcastleBuildEngineProvider.Paths.MRefBuilderConfig);
		}

		private bool ShouldSerializeHelp1xConfigFileName()
		{
			return !string.Equals(Help1xConfigFileName, SandcastleBuildEngineProvider.ProjectPaths.Help1xConfigFileName, StringComparison.OrdinalIgnoreCase);
		}

		private void ResetHelp1xConfigFileName()
		{
			Help1xConfigFileName = SandcastleBuildEngineProvider.ProjectPaths.Help1xConfigFileName;
		}

		private bool ShouldSerializeHelp2xConfigFileName()
		{
			return !string.Equals(Help2xConfigFileName, SandcastleBuildEngineProvider.ProjectPaths.Help2xConfigFileName, StringComparison.OrdinalIgnoreCase);
		}

		private void ResetHelp2xConfigFileName()
		{
			Help2xConfigFileName = SandcastleBuildEngineProvider.ProjectPaths.Help2xConfigFileName;
		}

		private bool ShouldSerializeHelp1xConceptualConfigFileName()
		{
			return !string.Equals(Help1xConceptualConfigFileName, SandcastleBuildEngineProvider.ProjectPaths.Help1xConceptualConfigFileName, StringComparison.OrdinalIgnoreCase);
		}

		private void ResetHelp1xConceptualConfigFileName()
		{
			Help1xConceptualConfigFileName = SandcastleBuildEngineProvider.ProjectPaths.Help1xConceptualConfigFileName;
		}

		private bool ShouldSerializeHelp2xConceptualConfigFileName()
		{
			return !string.Equals(Help2xConceptualConfigFileName, SandcastleBuildEngineProvider.ProjectPaths.Help2xConceptualConfigFileName, StringComparison.OrdinalIgnoreCase);
		}

		private void ResetHelp2xConceptualConfigFileName()
		{
			Help2xConceptualConfigFileName = SandcastleBuildEngineProvider.ProjectPaths.Help2xConceptualConfigFileName;
		}

		protected override void Dispose(bool disposing)
		{
			if (disposing)
			{
				if (help2xProject != null)
					help2xProject.Dispose();

				if (topicDesigner != null)
					topicDesigner.Dispose();
			}

			base.Dispose(disposing);
		}
		#endregion

		#region Event Handlers
		private static void topicManagement_AsyncError(object sender, AsyncCompletedEventArgs e)
		{
			TopicManager manager = (TopicManager) e.UserState;

			Log.Exception(e.Error, Resources.Text.TopicExplorerLogMessage, Environment.NewLine, manager.BuildOutput);

			if (DocProjectEnvironment.ShowErrorDialogWithCaption(System.Windows.Forms.MessageBoxButtons.RetryCancel, System.Windows.Forms.MessageBoxDefaultButton.Button2,
				Resources.Text.LoadError, Resources.Errors.TopicExplorerLoadError, Environment.NewLine, e.Error.GetType().FullName, e.Error.Message)
				== System.Windows.Forms.DialogResult.Retry)
			{
				manager.LoadTopics(true);
			}
		}
		#endregion

		#region Nested
		private sealed class BuildEngineProviderNameTypeConverter : TypeConverter
		{
			#region Constructors
			/// <summary>
			/// Constructs a new instance of the <see cref="BuildEngineProviderNameTypeConverter" /> class.
			/// </summary>
			public BuildEngineProviderNameTypeConverter()
			{
			}
			#endregion

			#region Methods
			public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
			{
				SandcastleProjectOptions options = (context == null) ? null : context.Instance as SandcastleProjectOptions;

				if (options != null && destinationType == typeof(string))
				{
					StringBuilder result = new StringBuilder((string) value);

					string sandcastleVersion = GetSandcastleVersion(options.Project);

					if (!string.IsNullOrEmpty(sandcastleVersion))
						result.AppendFormat(" {0}", sandcastleVersion);
					else
						result.Append(" (unknown version)");

					return result.ToString();
				}
				
				return base.ConvertTo(context, culture, value, destinationType);
			}
			#endregion
		}

		private sealed class TopicDesignerEditor : DocProjectOptionsDialogEditorBase<SandcastleProjectOptions, XmlContentDialog>
		{
			#region Constructors
			/// <summary>
			/// Constructs a new instance of the <see cref="TopicDesignerEditor" /> class.
			/// </summary>
			public TopicDesignerEditor()
			{
			}
			#endregion

			#region Methods
			protected override XmlContentDialog CreateDialog(SandcastleProjectOptions options, ITypeDescriptorContext context, IServiceProvider provider, object value)
			{
				if (options == null)
					throw new ArgumentNullException("options");

				XmlContentManager designer = options.TopicDesigner;

				// NOTE: Loading is required regardless of whether the dialog is being hosted in the properties window or the Tools Options page.

				// Load last committed values or saved content if none has been committed yet.
				designer.Load();

				return new XmlContentDialog(designer);
			}

			protected override bool HandleChanges(SandcastleProjectOptions options, XmlContentDialog form, System.Windows.Forms.DialogResult result)
			{
				if (options == null)
					throw new ArgumentNullException("options");

				// NOTE: Changes are committed/canceled using an internal buffer.  When hosted in the Tools Options page, this allows the dialog
				// to be opened and closed multiple times before the changes are finally committed or canceled (see SandcastleProjectOptions Commit and Cancel methods).

				XmlContentManager designer = options.TopicDesigner;

				if (result == System.Windows.Forms.DialogResult.OK)
				{
					IProjectItem media = options.Project.GetFolder(designer.MediaDirectoryRelative);

					// create the media directory if it doesn't exist
					media.Show();

					if (designer.ImportAllResources() > 0)
						// NOTE: refreshing the media folder to include images as project items only works while running inside of Visual Studio
						media.Refresh();

					designer.CommitChanges();

					return true;
				}
				else
				{
					designer.CancelChanges();
					
					return false;
				}
			}

			protected override object GetValue(SandcastleProjectOptions options, XmlContentDialog form, System.Windows.Forms.DialogResult result)
			{
				if (options == null)
					throw new ArgumentNullException("options");

				return options.TopicDesigner;
			}
			#endregion
		}

		private sealed class TopicManagementEditor : DocProjectOptionsDialogEditorBase<SandcastleProjectOptions, TopicManagementDialog>
		{
			#region Constructors
			/// <summary>
			/// Constructs a new instance of the <see cref="TopicManagementEditor" /> class.
			/// </summary>
			public TopicManagementEditor()
			{
			}
			#endregion

			#region Methods
			protected override TopicManagementDialog CreateDialog(SandcastleProjectOptions options, ITypeDescriptorContext context, IServiceProvider provider, object value)
			{
				if (options == null)
					throw new ArgumentNullException("options");

				return new TopicManagementDialog(options.TopicManagement);
			}

			protected override object EditValue(SandcastleProjectOptions options, ITypeDescriptorContext context, IServiceProvider provider, object value)
			{
				if (options == null)
					throw new ArgumentNullException("options");

				options.TopicManagement.BeginBufferChanges();

				return base.EditValue(options, context, provider, value);
			}

			protected override bool HandleChanges(SandcastleProjectOptions options, TopicManagementDialog form, System.Windows.Forms.DialogResult result)
			{
				if (options == null)
					throw new ArgumentNullException("options");

				// NOTE: Changes are committed/canceled using an internal buffer.  When hosted in the Tools Options page, this allows the dialog
				// to be opened and closed multiple times before the changes are finally committed or canceled (see SandcastleProjectOptions Commit and Cancel methods).

				if (result == System.Windows.Forms.DialogResult.OK)
				{
					options.TopicManagement.EndBufferChanges(true);
					return true;
				}
				else
				{
					options.TopicManagement.EndBufferChanges(false);
					return false;
				}
			}

			protected override object GetValue(SandcastleProjectOptions options, TopicManagementDialog form, System.Windows.Forms.DialogResult result)
			{
				if (options == null)
					throw new ArgumentNullException("options");

				return options.TopicManagement;
			}
			#endregion
		}

		private sealed class MissingDependenciesTypeConverter : TypeConverter
		{
			#region Constructors
			/// <summary>
			/// Constructs a new instance of the <see cref="MissingDependenciesTypeConverter" /> class.
			/// </summary>
			public MissingDependenciesTypeConverter()
			{
			}
			#endregion

			#region Methods
			public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
			{
				return destinationType == typeof(string);
			}

			public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
			{
				ICollection<SystemPath> dependencies = value as ICollection<SystemPath>;

				if (dependencies == null || destinationType != typeof(string))
					return base.ConvertTo(context, culture, value, destinationType);
				else
				{
					int directoryCount = 0, assemblyCount = 0;

					foreach (SystemPath dependency in dependencies)
					{
						if (dependency.IsDirectory)
							directoryCount++;
						else
							assemblyCount++;
					}

					return string.Format(culture, Resources.Text.MissingDependenciesPropertyText, dependencies.Count, directoryCount, assemblyCount);
				}
			}
			#endregion
		}

		private sealed class MissingDependenciesEditor : DocProjectOptionsDialogEditorBase<SandcastleProjectOptions, MissingDependenciesEditorDialog>
		{
			#region Constructors
			/// <summary>
			/// Constructs a new instance of the <see cref="MissingDependenciesEditor" /> class.
			/// </summary>
			public MissingDependenciesEditor()
			{
			}
			#endregion

			#region Methods
			protected override MissingDependenciesEditorDialog CreateDialog(SandcastleProjectOptions options, ITypeDescriptorContext context, IServiceProvider provider, object value)
			{
				if (options == null)
					throw new ArgumentNullException("options");

				return new MissingDependenciesEditorDialog(options.missingDependencies, options.Project);
			}

			protected override bool HandleChanges(SandcastleProjectOptions options, MissingDependenciesEditorDialog form, System.Windows.Forms.DialogResult result)
			{
				if (options == null)
					throw new ArgumentNullException("options");

				if (result == System.Windows.Forms.DialogResult.OK && form.HasChanges)
				{
					options.MissingDependencies = new List<SystemPath>(form.Dependencies);

					return true;
				}

				return false;
			}

			protected override object GetValue(SandcastleProjectOptions options, MissingDependenciesEditorDialog form, System.Windows.Forms.DialogResult result)
			{
				if (options == null)
					throw new ArgumentNullException("options");

				return options.missingDependencies;
			}
			#endregion
		}

		private sealed class VersionManagementTypeConverter : TypeConverter
		{
			#region Constructors
			/// <summary>
			/// Constructs a new instance of the <see cref="VersionManagementTypeConverter" /> class.
			/// </summary>
			public VersionManagementTypeConverter()
			{
			}
			#endregion

			#region Methods
			public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
			{
				return destinationType == typeof(string);
			}

			public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
			{
				SandcastleProjectOptions options = (context == null) ? null : context.Instance as SandcastleProjectOptions;
				ICollection<SourceVersion> versions = value as ICollection<SourceVersion>;

				if (versions == null || destinationType != typeof(string))
					return base.ConvertTo(context, culture, value, destinationType);
				else
				{
					int directoryCount = 0, assemblyCount = 0, fileCount = 0;
					List<string> uniqueVersions = new List<string>(versions.Count);

					if (options != null && options.sourceVersionNames != null)
					{
						uniqueVersions.Capacity += options.sourceVersionNames.Count;

						foreach (string version in options.sourceVersionNames.Values)
						{
							if (!uniqueVersions.Contains(version))
								uniqueVersions.Add(version);
						}
					}

					foreach (SourceVersion version in versions)
					{
						if (!uniqueVersions.Contains(version.Version))
							uniqueVersions.Add(version.Version);

						if (version.Source.IsDirectory)
							directoryCount++;
						else if (version.Source.IsAssembly)
							assemblyCount++;
						else
							fileCount++;
					}

					return string.Format(culture, Resources.Text.VersionsPropertyText, uniqueVersions.Count, directoryCount, assemblyCount, fileCount);
				}
			}
			#endregion
		}

		private sealed class VersionManagementEditor : DocProjectOptionsDialogEditorBase<SandcastleProjectOptions, VersionManagementDialog>
		{
			#region Constructors
			/// <summary>
			/// Constructs a new instance of the <see cref="VersionManagementEditor" /> class.
			/// </summary>
			public VersionManagementEditor()
			{
			}
			#endregion

			#region Methods
			protected override VersionManagementDialog CreateDialog(SandcastleProjectOptions options, ITypeDescriptorContext context, IServiceProvider provider, object value)
			{
				if (options == null)
					throw new ArgumentNullException("options");

				return new VersionManagementDialog(options.Versions, options.sourceVersionNames, options.Project);
			}

			protected override bool HandleChanges(SandcastleProjectOptions options, VersionManagementDialog form, System.Windows.Forms.DialogResult result)
			{
				if (options == null)
					throw new ArgumentNullException("options");

				if (result == System.Windows.Forms.DialogResult.OK && form.HasChanges)
				{
					if (options.BufferChanges)
					{
						options.versionsBuffer = form.Versions;
						options.sourceVersionNamesBuffer = form.SourceVersionNames;
					}
					else
						options.CommitVersions(form.Versions, form.SourceVersionNames);

					return true;
				}

				return false;
			}

			protected override object GetValue(SandcastleProjectOptions options, VersionManagementDialog form, System.Windows.Forms.DialogResult result)
			{
				if (options == null)
					throw new ArgumentNullException("options");

				return options.Versions;
			}
			#endregion
		}

		private sealed class ProduceHelp2xTypeConverter : ExpandableObjectConverter
		{
			#region Constructors
			/// <summary>
			/// Constructs a new instance of the <see cref="ProduceHelp2xTypeConverter" /> class.
			/// </summary>
			public ProduceHelp2xTypeConverter()
			{
			}
			#endregion

			#region Methods
			public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
			{
				return destinationType == typeof(string);
			}

			public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
			{
				Help2xProject project = value as Help2xProject;

				if (project == null || destinationType != typeof(string))
					return base.ConvertTo(context, culture, value, destinationType);
				else if (project.OutputEnabled)
					return Resources.Text.Help2xProjectPropertyTextEnabled;
				else
					return Resources.Text.Help2xProjectPropertyTextDisabled;
			}
			#endregion
		}

		private sealed class ProduceHelp2xEditor : DocProjectOptionsDialogEditorBase<SandcastleProjectOptions, Help2xProjectEditorDialog>
		{
			#region Constructors
			/// <summary>
			/// Constructs a new instance of the <see cref="ProduceHelp2xEditor" /> class.
			/// </summary>
			public ProduceHelp2xEditor()
			{
			}
			#endregion

			#region Methods
			protected override Help2xProjectEditorDialog CreateDialog(SandcastleProjectOptions options, ITypeDescriptorContext context, IServiceProvider provider, object value)
			{
				if (options == null)
					throw new ArgumentNullException("options");

				return new Help2xProjectEditorDialog(new Help2xProject(options.Help2xProject, false));
			}

			protected override bool HandleChanges(SandcastleProjectOptions options, Help2xProjectEditorDialog form, System.Windows.Forms.DialogResult result)
			{
				if (options == null)
					throw new ArgumentNullException("options");

				if (result == System.Windows.Forms.DialogResult.OK && form.Help2xProject.IsDirty)
				{
					if (options.BufferChanges)
						options.help2xProjectBuffer = form.Help2xProject;
					else
						options.CommitHelp2xProject(form.Help2xProject);

					return true;
				}

				return false;
			}

			protected override object GetValue(SandcastleProjectOptions options, Help2xProjectEditorDialog form, System.Windows.Forms.DialogResult result)
			{
				if (options == null)
					throw new ArgumentNullException("options");

				return options.Help2xProject;
			}
			#endregion
		}

		private sealed class ProduceHelp1xTypeConverter : ExpandableObjectConverter
		{
			#region Constructors
			/// <summary>
			/// Constructs a new instance of the <see cref="ProduceHelp1xTypeConverter" /> class.
			/// </summary>
			public ProduceHelp1xTypeConverter()
			{
			}
			#endregion

			#region Methods
			public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
			{
				return destinationType == typeof(string);
			}

			public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
			{
				Help1xProject project = value as Help1xProject;

				if (project == null || destinationType != typeof(string))
					return base.ConvertTo(context, culture, value, destinationType);
				else if (project.OutputEnabled)
					return Resources.Text.Help1xProjectPropertyTextEnabled;
				else
					return Resources.Text.Help1xProjectPropertyTextDisabled;
			}
			#endregion
		}

		private sealed class ProduceHelp1xEditor : DocProjectOptionsDialogEditorBase<SandcastleProjectOptions, Help1xProjectEditorDialog>
		{
			#region Constructors
			/// <summary>
			/// Constructs a new instance of the <see cref="ProduceHelp1xEditor" /> class.
			/// </summary>
			public ProduceHelp1xEditor()
			{
			}
			#endregion

			#region Methods
			protected override Help1xProjectEditorDialog CreateDialog(SandcastleProjectOptions options, ITypeDescriptorContext context, IServiceProvider provider, object value)
			{
				if (options == null)
					throw new ArgumentNullException("options");

				return new Help1xProjectEditorDialog(new Help1xProject(options.Help1xProject, false));
			}

			protected override bool HandleChanges(SandcastleProjectOptions options, Help1xProjectEditorDialog form, System.Windows.Forms.DialogResult result)
			{
				if (options == null)
					throw new ArgumentNullException("options");

				if (result == System.Windows.Forms.DialogResult.OK && form.Help1xProject.IsDirty)
				{
					if (options.BufferChanges)
						options.help1xProjectBuffer = form.Help1xProject;
					else
						options.CommitHelp1xProject(form.Help1xProject);

					return true;
				}

				return false;
			}

			protected override object GetValue(SandcastleProjectOptions options, Help1xProjectEditorDialog form, System.Windows.Forms.DialogResult result)
			{
				if (options == null)
					throw new ArgumentNullException("options");

				return options.Help1xProject;
			}
			#endregion
		}
		#endregion
	}
}
